﻿using ImageEffects;
using LogSystem;
using NodeCanvas.BehaviourTrees;
using NodeCanvas.Framework;
using NpcFramework;
using Pathfinding;
using Pathfinding.RVO;
using RootMotion.Dynamics;
using System;
using System.Collections.Generic;
using UnityEngine;
using Yoozoo.Core.Common;
using Yoozoo.Gameplay.Liberty.World;

namespace Yoozoo.Gameplay.Liberty.AI
{
    public class LibertyNpcBirthManager : 
        QualityListenerMono<LibertyNpcBirthManager.NpcCountQualitySetting>
    {

        [Serializable]
        public class NpcCountQualitySetting : QualitySetting
        {
            public int npcCount;
            public float vanishRadius;
        }

        public int npcCountLimited = 10;
        public float vanishRadius = 100;  // 动态Npc的消失半径，静态Npc由场景检测器控制消失和隐藏

        public override void ChangeQualitySetting(NpcCountQualitySetting qualitySetting)
        {
            npcCountLimited = qualitySetting.npcCount;
            vanishRadius = qualitySetting.vanishRadius;
        }

        
        [SerializeField]
        private GameObject _npcSeeker;

        public LightController lightController;
        
        private Dictionary<int, AIEntity> _aiEntities = new Dictionary<int, AIEntity>(50);

        public Dictionary<int, AIEntity> AIEntities
        {
            get
            {
                if (_aiEntities!=null)
                {
                    return _aiEntities;
                }
                return new Dictionary<int, AIEntity>();
            }
        }
        private Dictionary<Transform, AIEntity> _ray2AIEntityMap = new Dictionary<Transform, AIEntity>();
       
        public Dictionary<Collider, AIEntity> col2AIEntityMap = new Dictionary<Collider, AIEntity>();
        
        // npc隐藏
        private float checkTime = 0;
        private List<int> vanishList = new List<int>(10);
        [Tooltip("lookAt检测半径")]
        public float lookAtRadius = 8f;
        [Tooltip("lookAt检测角度")]
        public float lookAtAngle = 120f;
        private bool isIKFlag = false;

        private bool _showNpcStatus = true;
        
        // npc跟玩家距离计算
        private float distanceTimer = 0;
        [SerializeField]
        private float UpdateDistanceDuration = 0.1f;
        
        [SerializeField]
        private int lookAtPercent = 70;
        
        private void Update()
        {
            if (LibertyAIUtils.PlayerController == null || 
                LibertyAIUtils.GetPlayerEntity().viewer == null || 
                LibertyAIUtils.GetPlayerEntity().viewer.animator == null)
            {
                return;
            }

            // 更新NPC距离玩家距离
            distanceTimer += Time.deltaTime;
            if (distanceTimer > UpdateDistanceDuration)
            {
                distanceTimer -= UpdateDistanceDuration;

                Vector3 playerPosition = LibertyAIUtils.GetPlayerEntity().viewer.animator.transform.position;
                Vector3 npcPosition;
                foreach (var aiEntity in _aiEntities.Values)
                {
                    npcPosition = aiEntity.viewer.animator.transform.position;
                    aiEntity.data.directionToPlayer.x = playerPosition.x - npcPosition.x;
                    aiEntity.data.directionToPlayer.z = playerPosition.z - npcPosition.z;
                    // 距离
                    var distanceToPlayer = aiEntity.data.directionToPlayer.magnitude;
                    aiEntity.data.distanceToPlayer = distanceToPlayer;
                    // 方向
                    aiEntity.data.directionToPlayer = aiEntity.data.directionToPlayer.normalized;

                    // 设置LOD
                    if (aiEntity.lodController == null)
                    {
                        // 这是主角
                    }
                    else
                    {
                        if (distanceToPlayer < LibertyConst.NpcLod0Radius)
                        {
                            aiEntity.lodController?.ChangeLod(0);
                            aiEntity.weapon?.ChangeLod(0);
                        }
                        else if (distanceToPlayer < LibertyConst.NpcLod1Radius)
                        {
                            aiEntity.lodController?.ChangeLod(1);
                            aiEntity.weapon?.ChangeLod(1);
                        }
                        else
                        {
                            aiEntity.lodController?.ChangeLod(2);
                            aiEntity.weapon?.ChangeLod(2);
                        }
                    }
                }
            }
            
            checkTime += Time.deltaTime;
            if (checkTime > 5f) // 5秒调用一次动态Npc的隐藏检测
            {
                vanishList.Clear();
                Vector3 playerPos = LibertyAIUtils.PlayerController.GetPlayerPosition();
                int vc = 0;
                foreach (var aiEntity in _aiEntities)
                {
                    if(LibertyAIUtils.GetPlayerEntity() == aiEntity.Value) continue;
                    if (aiEntity.Value.data.isStreetEvent) continue;    // 街道Npc不做处理
                    if ((aiEntity.Value.data.npcStatus & NpcStatus.InCar)> 0) continue; // 车内的npc销毁由车辆那边控制

                    if (aiEntity.Value.data.distanceToPlayer > vanishRadius)
                    {
                        vanishList.Add(aiEntity.Key);
                        vc++;
                        if (vc > 3) // 每次最多移除 3 个
                        {
                            break;
                        }
                    }
                }

                foreach (var i in vanishList)
                {
                    Debug.Log(LogModule.LibertyScene,$"自动移除动态Npc：{i}");
                    RemoveNpc(i);
                }
                
                checkTime = 0;
            }
            
        }

        public AIEntity GetNpc(int uniqueId)
        {
            if (_aiEntities.TryGetValue(uniqueId,out var aiEntity))
            {
                return aiEntity;
            }
            return null;
        }
        
        public bool AddAIEntity(AIEntity aiEntity)
        {
            int uniqueId = aiEntity.data.uniqueId;
            if (_aiEntities.ContainsKey(uniqueId) && 
                _aiEntities[uniqueId] != null)
            {
                return false;
            }
            _aiEntities[uniqueId] = aiEntity;
            return true;
        }
        
        public AIEntity GetAIEntity(Transform hitTrans)
        {
            if (hitTrans == null) {
                return null;
            }
            _ray2AIEntityMap.TryGetValue(hitTrans, out AIEntity aiEntity);
            return aiEntity;
        }
        
        public bool AddNpc(AIBirthData birthData,GameObject graph)
        {
            if (!_showNpcStatus)
            {
                return false;
            }
            
            if (_aiEntities.ContainsKey(birthData.uniqueId))
            {
                return false;
            }

            if (!birthData.isFare && !birthData.isStreetEvent)  // 乘客不受Npc数量限制
            {
                int c = 0;
                foreach (var entity in _aiEntities)
                {
                    if (entity.Value.data.npcStyle != NpcStyle.Fare && !entity.Value.data.isStreetEvent)
                    {
                        c++;
                    }
                }
                
                if (c >= npcCountLimited)
                {
                    return false;
                }
            }


            NpcStyle npcStyle = NpcStyle.None;
            if (birthData.isFare)
            {
                npcStyle |= NpcStyle.Fare;
            }
            else if (birthData.isStatic)
            {
                npcStyle |= NpcStyle.Static;
            }
            else if (birthData.isStreetEvent)
            {
                npcStyle |= NpcStyle.StreetEvent;
            }
            else
            {
                npcStyle |= NpcStyle.PathWalker;
            }
            
            int pathId = birthData.pathId;
            int pathPointIndex = birthData.pathPointIndex;
            PathDirection pathDirection = PathDirection.Forward;
            
            if (birthData.isFindNearlyPath)
            {
                var point = LibertyAIUtils.TrafficManager.PathManager.GetNearestNavPoint(birthData.position, birthData.forward,
                    out pathDirection);
                if (point!=null)
                {
                    pathId = point.pathId;
                    pathPointIndex = point.pointIndex;
                }
            }

            if ((npcStyle & NpcStyle.PathWalker) > 0)
            {
                var pathState = WorldState.Ws.GetNpcPathState(pathId);
                if (pathState!=null)
                {
                    if (pathState.GetNpcCount()>= (pathState.pathData.totalLength / WorldState.Ws.npcUnitLength))
                    {
                        // 已经超过了当前路径行走npc的容纳数量
                        return false;
                    }
                }

                // 平均一下两个方向的Npc
                if (pathState.GetDirNpcCount(PathDirection.Forward) > pathState.GetDirNpcCount(PathDirection.Backward))
                {
                    pathDirection = PathDirection.Backward;
                }
                else
                {
                    pathDirection = PathDirection.Forward;
                }
            }
            
            AIEntity aiEntity = new GameObject($"AIEntity_{birthData.uniqueId}").AddComponent<AIEntity>();
            aiEntity.transform.parent = this.transform;

            aiEntity.id = birthData.uniqueId;

            // Controller （状态机、行为树）
            BehaviourTreeOwner graphOwner = graph.GetComponent<BehaviourTreeOwner>();
            Blackboard blackboard = graph.GetComponent<Blackboard>();
            blackboard.SetVariableValue("UniqueID", birthData.uniqueId);
            blackboard.SetVariableValue("AIEntity", aiEntity);
            aiEntity.graph = graphOwner;
            aiEntity.blackboard = blackboard;
            graph.transform.SetParent(aiEntity.transform);
            // Controller（寻路组件）
            var seeker = GameObject.Instantiate(_npcSeeker).GetComponent<LibertyNpcSeeker>();
            aiEntity.seeker = seeker;
            seeker.transform.SetParent(aiEntity.transform);

            _ray2AIEntityMap.Add(seeker.transform,aiEntity);
            col2AIEntityMap.Add(seeker.CharacterController,aiEntity);
            
            // 设置寻路层
            seeker.Seeker.graphMask = GraphMask.FromGraphName("sidewalk");

            // View
            int modelId = LoadNpc(birthData.uniqueId, birthData.modelResId, birthData.randomModel,
                out GameObject npcModel);
            if (npcModel == null)
            {
                Debug.LogError(LogModule.LibertyScene,"加载Npc失败：" + birthData.uniqueId.ToString());
                return false;
            }
            //use ragdoll
//            if (modelId <= 5)
//            {
            try
            {
                npcModel = npcModel.transform.GetChild(2).gameObject;
            }
            catch (Exception e)
            {
                string locak= "";
            }
                
//            }
            LightElement lightElement = npcModel.GetComponentInChildren<LightElement>();
            if (lightController && lightElement)
            {
                lightController.AddLightElement(lightElement);
            }
            
            // TODO 待优化，GetComponent操作太多
            float speedValue = 0.2f;
            if (birthData.customSpeed)
            {
                seeker.SetMaxSpeed(birthData.maxSpeed);
                speedValue = birthData.maxSpeed;
            }
            else
            {
                SeekerSpeed seekerSpeed = npcModel.GetComponent<SeekerSpeed>();
                if (seekerSpeed != null) speedValue = seekerSpeed.Speed;
                seeker.SetMaxSpeed(speedValue);
            }

            seeker.SetPriority(0.5f);
            
            Animator animator = npcModel.GetComponent<Animator>();
            animator.updateMode = AnimatorUpdateMode.AnimatePhysics;

            LocomotionAgent agent = npcModel.GetComponent<LocomotionAgent>();
            agent.animHorName = "WalkH";
            agent.animVerName = "WalkV";
            agent.animSpeedName = "";
            agent.GetAnimId();
            if (!agent.enabled)
            {
                agent.enabled = true;
            }

            //agent.enabled = !birthData.isStatic;
            
            BoneItemController boneItemController = npcModel.GetComponent<BoneItemController>();
            
            GameObject npcParent = new GameObject($"Viewer_{aiEntity.id}");
            npcParent.transform.SetParent(aiEntity.transform);

            // 落到地面上
            Vector3 pos = birthData.position;
            if (Physics.Raycast(pos + new Vector3(0,1f,0), Vector3.down, out var hitInfo))
            {
                pos.y = hitInfo.point.y;
            }
            npcParent.transform.position = pos;
            
            npcParent.transform.rotation = Quaternion.LookRotation(birthData.forward, Vector3.up);
            //use ragdoll
//            if (modelId <= 5)
            {
                Transform npcModelRoot = npcModel.transform.parent;
                npcModelRoot.SetParent(npcParent.transform);
                npcModelRoot.localPosition = Vector3.zero;
                npcModelRoot.localRotation = Quaternion.identity;
                aiEntity.puppetMaster = npcModelRoot.GetChild(1).GetComponent<PuppetMaster>();
                aiEntity.puppetMaster.mode = PuppetMaster.Mode.Disabled;
                seeker.SetCurAiEntity(aiEntity);
                NpcPuppetMaster master = npcModel.GetComponent<NpcPuppetMaster>();
                master.SetCurAiEntity(aiEntity);
                agent.SyncTransform = npcParent.transform;
            }
//            else
//            {
//                npcModel.transform.SetParent(npcParent.transform);
//            }
            npcModel.transform.localPosition = Vector3.zero;
            npcModel.transform.localRotation = Quaternion.identity;

            aiEntity.viewer = new AIViewer()
            {
                transform = npcModel.transform,
                viewerRoot = npcParent.transform,
                animator =  animator,
                boneItemController = boneItemController,
                locomotionAgent = agent,
            };

           
            // 偏移因子
            float pathOffsetFactor = Mathf.Min(1.0f,UnityEngine.Random.Range(0.35f, 1.2f));// < 0.5f ? 0.5f : 1f;
            if (birthData.customPathOffset)
            {
                pathOffsetFactor = birthData.pathOffsetFactor;
            }
            
            // Data
            aiEntity.data = new AIData()
            {
                uniqueId = birthData.uniqueId,
                maxSpeed = speedValue,
                position = birthData.position,
                CurrentLerpRotation = Quaternion.LookRotation(birthData.forward).eulerAngles,
                
                side = birthData.sideType,    // 阵营
                isBoss = birthData.isBoss,  // 是否是Boss
                
                pathDirection = pathDirection,
                pathId = pathId,
                pathPointIndex = pathPointIndex,
                
                animState = birthData.animState,
                
                pathOffsetFactor = pathOffsetFactor,
                
                npcStyle = npcStyle,
                
                isStreetEvent = birthData.isStreetEvent,
            };
            aiEntity.SetPositionAndRotation(birthData.position,Quaternion.LookRotation(birthData.forward).eulerAngles);
            aiEntity.BirthData = birthData;
            aiEntity.rvoController = aiEntity.seeker.GetComponent<RVOController>();
            aiEntity.characterController = aiEntity.seeker.GetComponent<CharacterController>();
            aiEntity.setPropertyDataConfig(LibertyConfigManager.GetInstance().GetAIPropertyDataConfig(modelId));
  
            agent.RegistSeeker(seeker,aiEntity,aiEntity.characterController);
            
            seeker.RegisterAgent(npcModel.transform, agent);
            seeker.SetForceUpdateTargetRotation(aiEntity.data.rotation);
            seeker.SetPosition(aiEntity.data.position);
            seeker.transform.forward = birthData.forward;
            
            AnimationAgent animationAgent = npcModel.GetComponent<AnimationAgent>();
            animationAgent.InitData(aiEntity);
            animationAgent.SetDisableOperate(false);
            aiEntity.viewer.animationAgent = animationAgent;


            BattleAgent battleAgent = npcModel.GetComponent<BattleAgent>();
            battleAgent.InitData(aiEntity);
            aiEntity.battleAgent = battleAgent;

            string strAIEntityId = aiEntity.id.ToString();
            string strSide = birthData.sideType.ToString();
            // 伤害触发器
            Collider onHitTrigger = battleAgent.onHitCollider;
            GameObject onHitGameObject = onHitTrigger.gameObject;
            onHitGameObject.name = strAIEntityId;
            onHitGameObject.tag = strSide;
            onHitTrigger.enabled = true;
            aiEntity.onHitCollider = onHitTrigger;
            // 头部伤害触发器
            Collider headTrigger = battleAgent.headCollider;
            GameObject headGameObject = headTrigger.gameObject;
            headGameObject.name = strAIEntityId;
            headGameObject.tag = strSide;
            headTrigger.enabled = true;
            aiEntity.headCollider = headTrigger;
            
            // 伤害触发器脚本
            NpcTrigger npcTrigger = onHitGameObject.GetComponent<NpcTrigger>();
            npcTrigger.InitData(aiEntity);
            aiEntity.puppetMaster.OnDeath = npcTrigger.OnNpcDeath;
            aiEntity.npcTrigger = npcTrigger;
            if ((npcStyle & NpcStyle.Static) > 0 || (npcStyle & NpcStyle.StreetEvent) > 0)
            {
                // 静态角色不考虑移动相关参数
                //seeker.aiPath.enabled = false;
                //seeker.CharacterController.enabled = false;
                // seeker.npcCaster.gameObject.SetActive(false);
                //seeker.enabled = false;
                //seeker.RvoController.enabled = false;
                //seeker._IsEnabled = false;

                aiEntity.data.npcStatus = NpcStatus.StaticAction;
                aiEntity.data.defaultNpcStatus = NpcStatus.StaticAction;
            }
            else if ((npcStyle & NpcStyle.Fare) > 0)
            {
                //aiEntity.data.npcStatus = NpcStatus.InCar;
                //seeker.npcCaster.gameObject.SetActive(false);
                
                // TODO 关闭碰撞、寻路等逻辑
                
            }
            else
            {
                aiEntity.data.npcStatus = NpcStatus.PathControl;
                aiEntity.data.defaultNpcStatus = NpcStatus.PathControl;
                WorldState.Ws.AddToPath(birthData.uniqueId,pathId);
            }
            aiEntity.data.npcStyle = npcStyle;

            //LookAtIK组件添加
            if (birthData.lookAtType == 2 || (birthData.lookAtType == 3 && UnityEngine.Random.Range(0, 100) <= lookAtPercent))
            {
                animationAgent.SetSupportLookAtPlayer(true);
            }
            else
            {
                animationAgent.SetSupportLookAtPlayer(false);
            }

            var lodController = npcModel.GetComponent<LibertyNpcLodController>();
            if(lodController)
                lodController.ChangeLod(0);
            aiEntity.lodController = lodController;


            // aiEntity.puppetMaster.state = PuppetMaster.State.Alive;
            // aiEntity.puppetMaster.mode = PuppetMaster.Mode.Disabled;
            aiEntity.puppetMaster.enabled = true;
             // graphOwner.StartBehaviour();
             graph.gameObject.SetActive(true);

            _aiEntities[birthData.uniqueId] = aiEntity;

            return true;
        }


        public int LoadNpc(int uniqueId,int npcId, bool random, out GameObject v)
        {
            var npcManager = LibertyManager.GetInstance("LibertyManager").NpcManager;
            int nId = npcManager.AddNpc(npcId, random, uniqueId, Vector3.zero, Quaternion.identity);
            v = null;
            if (nId >= 0)
                v = npcManager.GetNpc(uniqueId);
            return nId;
        }


        public bool RemoveNpc(int uniqueId)
        {
            _aiEntities.TryGetValue(uniqueId, out AIEntity aiEntity);
            if (aiEntity)
            {
                aiEntity.gameObject.SetActive(false);
                // if (aiEntity.inPlayerRadar) {
                //     return false;
                // }
                // 卸下装备
                aiEntity.battleAgent.ChangeWeapon(0);
                if (aiEntity.data.pathId > 0)
                {
                    WorldState.Ws.RemoveFromPath(uniqueId,aiEntity.data.pathId);
                }
                LibertyAIUtils.AIManager.ReverseGraph(aiEntity.id);
                _aiEntities.Remove(uniqueId);
                _ray2AIEntityMap.Remove(aiEntity.seeker.transform);
                LibertyNpcSeeker npcSeeker = aiEntity.seeker as LibertyNpcSeeker;
                if (npcSeeker) {
                    col2AIEntityMap.Remove(npcSeeker.CharacterController);
                }
                //如果在场景中离开,这个可能报错,这个报错之后,整个Editor期间就错了(整个)
                if(aiEntity.viewer!=null && aiEntity.viewer.locomotionAgent!=null)
                    aiEntity.viewer.locomotionAgent.enabled = true;
                if(aiEntity.characterController!=null)
                    aiEntity.characterController.enabled = true;
                if(aiEntity.seeker!=null)
                    aiEntity.seeker.transform.gameObject.SetActive(true);
                var npcManager = LibertyManager.GetInstance("LibertyManager").NpcManager;
                npcManager.RemoveNpc(aiEntity.id);

                GameObject.Destroy(aiEntity.gameObject);
                if (aiEntity.npcTrigger)
                {
                    aiEntity.npcTrigger = null;
                }
                return true;
            }
            return false;
        }

        public void EnableAllNpc()
        {
            _showNpcStatus = true;
            foreach (var entity in _aiEntities)
            {
                if (entity.Value && entity.Value.gameObject)
                {
                    if (entity.Value.id != LibertyAIUtils.PLAYER_AI_ID)
                    {
                        entity.Value.gameObject.SetActive(true);
                    }
                }
            }
        }
        
        public void DisableAllNpc()
        {
            _showNpcStatus = false;
            foreach (var entity in _aiEntities)
            {
                if (entity.Value && entity.Value.gameObject)
                {
                    if (entity.Value.id != LibertyAIUtils.PLAYER_AI_ID)
                    {
                        entity.Value.gameObject.SetActive(false);
                    }
                }
            }
        }
        
        
        public void Clear()
        {
            if (_aiEntities!=null)
            {
                AIEntity playerEntity = null;
                foreach (var entity in _aiEntities)
                {
                    if (entity.Value && entity.Value.gameObject)
                    {
                        if (entity.Value.id != LibertyAIUtils.PLAYER_AI_ID)
                        {
                            
                            var npcManager = LibertyManager.GetInstance("LibertyManager").NpcManager;
                            npcManager.RemoveNpc(entity.Value.id);
                            GameObject.Destroy(entity.Value.gameObject);
                        }
                        else
                        {
                            playerEntity = entity.Value;
                        }
                        
                    }
                }
                _aiEntities.Clear();
                if (playerEntity!=null)
                {
                    _aiEntities.Add(playerEntity.id,playerEntity);
                }
                _ray2AIEntityMap.Clear();
                col2AIEntityMap.Clear();
            }
        }
        
        public void Dispose()
        {
            
            _aiEntities.Clear();
        }

#if UNITY_EDITOR
        void OnDrawGizmos()
        {
            if (LibertyAIUtils.PlayerController == null ||
                LibertyAIUtils.GetPlayerEntity().viewer == null ||
                LibertyAIUtils.GetPlayerEntity().viewer.animator == null)
            {
                return;
            }

            var playerPosition = LibertyAIUtils.GetPlayerEntity().viewer.animator.transform.position;
            var color = Gizmos.color;
            Gizmos.color = Color.green;
            Gizmos.DrawWireSphere(playerPosition, LibertyConst.NpcLod0Radius);
            Gizmos.color = Color.yellow;
            Gizmos.DrawWireSphere(playerPosition, LibertyConst.NpcLod1Radius);
            Gizmos.color = color;
        }
#endif
    }
}
